﻿using System;
using System.Security.Cryptography;
using System.Text;

namespace Project32CHE.Passport.Utilities
{
    internal class EncryptOperator : IDisposable
    {
        private static Random random = new Random();

        private static string Bytes2Hex(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0, j = data.Length; i < j; i++)
            {
                sb.Append(data[i].ToString("X2"));
            }
            return sb.ToString();
        }
        private static byte[] Hex2Bytes(string hex)
        {
            if (hex.Length % 2 == 1)
            {
                throw new ArgumentException();
            }
            byte[] bs = new byte[hex.Length / 2];
            for (int i = 0, j = bs.Length; i < j; i++)
            {
                bs[i] = byte.Parse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return bs;
        }

        protected byte[] Salt { get; set; }

        public EncryptOperator(byte[] salt) { this.RefreshSalt(salt); }
        public EncryptOperator(string salt) : this(Hex2Bytes(salt)) { }
        public EncryptOperator() { this.RefreshSalt(null); }

        protected virtual void RefreshSalt(byte[] salt)
        {
            if (salt == null)
            {
                salt = new byte[16];
                random.NextBytes(salt);
            }
            this.Salt = salt;
        }
        protected virtual byte[] EncryptCore(string input)
        {
            var data = System.Text.Encoding.UTF8.GetBytes(input);
            var salt = this.Salt;
            for (int i = 0, j = 0, dataLength = data.Length, saltLength = salt.Length; i < dataLength; i++, j++)
            {
                if (j >= saltLength) { j = 0; }
                data[i] ^= salt[j];
            }
            using (var md5 = new MD5CryptoServiceProvider())
            {
                var hash = md5.ComputeHash(data);
                return hash;
            }
        }

        public string Encrypt(string input, out string salt)
        {
            salt = Bytes2Hex(this.Salt);
            byte[] bs = this.EncryptCore(input);
            string encrypt = Bytes2Hex(bs);
            return encrypt;
        }
        public bool Check(string input, string encrypt)
        {
            byte[] oldencrypt = Hex2Bytes(encrypt);
            byte[] newencrypt = this.EncryptCore(input);
            int diff = oldencrypt.Length ^ newencrypt.Length;
            for (int i = 0, j = oldencrypt.Length, k = newencrypt.Length; i < j && i < k; i++)
            { diff |= (oldencrypt[i] ^ newencrypt[i]); }
            return (diff == 0);
        }

        #region IDisposable 成员

        public void Dispose()
        {
        }

        #endregion
    }
}
